﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
using Microsoft.WindowsAPICodePack.Dialogs;
using System.Reflection;
using System.Timers;
using System.IO;
using System.Diagnostics;
using Microsoft.WindowsAPICodePack.Shell;
using Microsoft.WindowsAPICodePack.Taskbar;
using System.Runtime.Remoting.Messaging;
using System.Resources;
using System.Globalization;
using System.IO.MemoryMappedFiles;

namespace ArmoryViewer
{
    public class App : Application
    {
        public MainWindow AppWindow { get; private set; }
        private Program program = null;

        public static ResourceManager ResourceManager = new ResourceManager("ArmoryViewer.Resources.Localization.Localization", Assembly.GetExecutingAssembly());

        static string appVersion;
        bool done = false;
        bool updatecommand = false;
        static ProgressWindow pgs = new ProgressWindow();

        public static bool IsInstalled
        {
            get { return File.Exists(App.CurrentDirectory + "installed"); }
        }

        public static bool IsAdmin
        {
            get { return (VistaTools.IsReallyVista() && VistaTools.IsElevated()) || !VistaTools.IsReallyVista(); }
        }

        public static bool IsVista
        {
            get { return VistaTools.IsReallyVista(); }
        }

        public static bool IsWin7
        {
            get { return IsVista && Environment.OSVersion.Version.Minor >= 1; }
        }

        /// <summary>
        /// Gets the application version.
        /// </summary>
        /// <value>The application version.</value>
        public static string Version
        {
            get { return appVersion; }
        }

        public static string PreferredDirectory
        {
            get
            {
                if (IsInstalled) return AppDataDirectory;
                else return CurrentDirectory;
            }
        }

        /// <summary>
        /// Gets the current directory.
        /// </summary>
        /// <value>The current directory.</value>
        public static string CurrentDirectory
        {
            get { return AppDomain.CurrentDomain.BaseDirectory; }
        }

        public static string AppDataDirectory
        {
            get
            {
                return 
                  Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + Path.DirectorySeparatorChar +
                  "Beastware" + Path.DirectorySeparatorChar +
                  "Armory Viewer" + Path.DirectorySeparatorChar;
            }
        }

        /// <summary>
        /// Shows the error dialog.
        /// </summary>
        /// <param name="exception">The exception.</param>
        public static void ShowErrorDialog(Exception exception)
        {
            ErrorWindow err = new ErrorWindow(exception);
        }

        public App(Program program) : base() 
        {
            this.program = program;
        }

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            AppWindow = new MainWindow();

            Settings.Load();

            CleanUpFiles();

            // Set App Version
            appVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();

            ProcessArgs(e.Args, true);

            // Check for updates
            if (Settings.UpdateOnStartup && !updatecommand) CheckForUpdates();

            AppWindow.Show();

            CreateJumpList();
        }

        public static void CleanUpFiles()
        {
            try
            {
                string[] paths = { AppDataDirectory, CurrentDirectory };
                foreach (string path in paths)
                {
                    if (File.Exists(path + "updater.exe")) { File.Delete(path + "updater.exe"); }
                    if (File.Exists(path + "aviewer.zip")) { File.Delete(path + "aviewer.zip"); }
                    if (File.Exists(path + "aviewer.exe")) { File.Delete(path + "aviewer.exe"); }
                    if (File.Exists(path + "7z.exe")) { File.Delete(path + "7z.exe"); }
                    if (File.Exists(path + "Armory Viewer.exe")) { File.Delete(path + "Armory Viewer.exe"); }
                }
            }
            catch (UnauthorizedAccessException)
            {
                return;
            }
        }
        
        void CreateJumpList()
        {
            if (TaskbarManager.IsPlatformSupported)
            {
                try
                {
                    JumpList jl = JumpList.CreateJumpList();
                    jl.KnownCategoryToDisplay = JumpListKnownCategoryType.Neither;
                    JumpListCustomCategory catRecent = new JumpListCustomCategory("Recent");
                    catRecent.AddJumpListItems
                    (
                        new JumpListLink(Assembly.GetEntryAssembly().Location, "Search: Test")
                        {
                            Arguments = "/search Test",
                            IconReference = new IconReference(Assembly.GetEntryAssembly().Location, 0)
                        }
                    );
                    jl.AddCustomCategories(catRecent);
                    jl.Refresh();
                }
                catch (InvalidOperationException)
                {
                    // Fix for application shutdown bug
                    return;
                }
            }
        }

        public void ProcessArgs(string[] args, bool isFirstInstance)
        {
            if (args.Length <= 0) return;

            string arg = args[0].ToLower();
            if (arg == "/update")
            {
                if (IsAdmin)
                {
                    Update();
                    updatecommand = true;
                }
                else
                {
                    RunAsAdmin(arg);
                }
            }
            else if (arg == "/search" && args.Length >= 2)
            {
                AppWindow.Search(args[1]);
            }
            else if (arg.StartsWith("av:"))
            {
                if (arg.Contains("character-sheet.xml"))
                // Load Character
                {
                    try
                    {
                        string name, realm;
                        if (arg.IndexOf("r=") > arg.IndexOf("n="))
                        {
                            name = arg.Substring(arg.IndexOf("n="), arg.IndexOf("&") - arg.IndexOf("n="));
                            realm = arg.Substring(arg.IndexOf("r="));
                        }
                        else
                        {
                            realm = arg.Substring(arg.IndexOf("r="), arg.IndexOf("&") - arg.IndexOf("r="));
                            name = arg.Substring(arg.IndexOf("n="));
                        }
                        AppWindow.LoadCharacter(name.Substring(2), realm.Substring(2));
                    }
                    catch
                    {
                    }
                }
            }
            else
            {
                MessageBox.Show(arg);
            }
        }

        public static void CheckForUpdates(bool forceUpdate=false)
        {
            // Update is available, ask to update.
            if (forceUpdate || Updater.UpdateAvailable)
            {
                if (IsVista)
                    try { ShowUpdateTaskDlg(); }
                    catch { ShowUpdatePrompt(); }
                else
                    ShowUpdatePrompt();
            }
        }

        static void ShowUpdateTaskDlg()
        {
            TaskDialog dlg = new TaskDialog();
            dlg.Caption = ResourceManager.GetString("UpdateAvailable", CultureInfo.CurrentCulture);
            dlg.Icon = TaskDialogStandardIcon.Information;
            dlg.InstructionText = ResourceManager.GetString("UpdateAvailableDesc", CultureInfo.CurrentCulture);
            dlg.OwnerWindowHandle = Process.GetCurrentProcess().MainWindowHandle;
            dlg.StartupLocation = TaskDialogStartupLocation.CenterOwner;
            TaskDialogCommandLink yes = new TaskDialogCommandLink("yes", ResourceManager.GetString("Yes", CultureInfo.CurrentCulture), ResourceManager.GetString("YesUpdateDesc", CultureInfo.CurrentCulture)) { Default = true, ShowElevationIcon = true };
            yes.Click += new EventHandler(yes_Click);
            TaskDialogCommandLink no = new TaskDialogCommandLink("no", ResourceManager.GetString("No", CultureInfo.CurrentCulture), ResourceManager.GetString("NoUpdateDesc", CultureInfo.CurrentCulture));
            no.Click += delegate(object sender, EventArgs e)
            {
                dlg.Close();
            };
            dlg.Controls.Add(yes);
            dlg.Controls.Add(no);
            dlg.Show();
        }

        static void yes_Click(object sender, EventArgs e)
        {
            if (sender is TaskDialogCommandLink) { ((TaskDialog)(sender as TaskDialogCommandLink).HostingDialog).Close(); }
            RunWithCommand("/update");
        }

        static void RunWithCommand(string args)
        {
            Process proc = new Process();
            proc.StartInfo.FileName = System.Reflection.Assembly.GetExecutingAssembly().Location;
            proc.StartInfo.UseShellExecute = true;
            proc.StartInfo.Arguments = args;
            try { proc.Start(); }
            catch { return; }
        }

        public static void SetMultiInstance(bool multiInstance)
        {
            using (MemoryMappedFile MemoryMappedFile = MemoryMappedFile.CreateNew("AV-multi", 1024))
            {
                MemoryMappedViewStream stream = MemoryMappedFile.CreateViewStream();
                using (BinaryWriter writer = new BinaryWriter(stream))
                {
                    writer.Write(multiInstance.ToString());
                }
            }
        }

        static void RunAsAdmin(string args)
        {
            SetMultiInstance(true);

            Process proc = new Process();
            proc.StartInfo.FileName = System.Reflection.Assembly.GetExecutingAssembly().Location;
            proc.StartInfo.UseShellExecute = true;
            proc.StartInfo.Arguments = args;
            if (IsVista) proc.StartInfo.Verb = "runas";
            try { proc.Start(); }
            catch { return; }
            Application.Current.Shutdown();
        }

        static void ShowUpdatePrompt()
        {
            MessageBoxResult rslt = MessageBox.Show(ResourceManager.GetString("UpdateAvailableDesc", CultureInfo.CurrentCulture), ResourceManager.GetString("UpdateAvailable", CultureInfo.CurrentCulture), MessageBoxButton.YesNo, MessageBoxImage.Information, MessageBoxResult.No);
            if (rslt == MessageBoxResult.Yes) yes_Click(rslt, new EventArgs());
        }

        private void ProcessCommand(string arg)
        {
            switch (arg)
            {
                case "/update": Update(); updatecommand = true; return;
                default: break;
            }
        }

        private void Update()
        {
            Updater.UpdateProgressChanged += new Updater.UpdateProgressChangedEventHandler(Updater_UpdateProgressChanged);
            Updater.UpdateCompleted += new Updater.UpdateCompletedEventHandler(Updater_UpdateCompleted);
            pgs = new ProgressWindow();
            pgs.Caption = "Downloading Update";
            pgs.Text = "Determining what to do...";
            pgs.TaskDialogClosing += dlg_Closing;
            pgs.Closing += new System.ComponentModel.CancelEventHandler(pgs_Closing);

            Updater.DownloadUpdate();
            pgs.ShowMe();

            if (done) Application.Current.Shutdown();
        }



        /// <summary>
        /// Handles the UpdateCompleted event of the Updater control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        void Updater_UpdateCompleted(object sender, EventArgs e)
        {
            try
            {
                Process updater = new Process();
                updater.StartInfo.FileName = PreferredDirectory + "updater.exe";
                updater.StartInfo.CreateNoWindow = true;
                updater.Start();
                done = true;
                pgs.CloseDialog();
            }
            catch (Exception ex)
            {
                ShowErrorDialog(ex);
            }
        }

        void Updater_UpdateProgressChanged(object sender, UpdateProgressChangedEventArgs e)
        {
            pgs.CurrentValue = e.Progress;
            if (e.Message != null)
            {
                // Show error
                if (e.Message == "error")
                {
                    pgs.ProgressState = TaskDialogProgressBarState.Error;
                    pgs.Caption = "Update Failed";
                    pgs.Text = e.Error.Message;
                    pgs.StandardButtons = TaskDialogStandardButtons.Close;
                }
                // Show message
                else
                    pgs.Text = e.Message;
            }
        }

        bool Update_Closing()
        {
            if (pgs.StandardButtons == TaskDialogStandardButtons.Cancel && !done)
            {
                if (MessageBox.Show("Are you sure you wish to cancel the update?", "Armory Viewer", MessageBoxButton.YesNo) == MessageBoxResult.No)
                    return true;
                else
                    Updater.CancelUpdate();
            }
            return false;
        }

        void pgs_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            e.Cancel = Update_Closing();
        }

        void dlg_Closing(object sender, TaskDialogClosingEventArgs e)
        {
            e.Cancel = Update_Closing();
        }
    }
}
